This is an R Markdown Notebook. When you execute code within the notebook, the results appear beneath the code.

Try executing this chunk by clicking the Run button within the chunk or by placing your cursor inside it and pressing Ctrl+Shift+Enter.

plot(cars)

Add a new chunk by clicking the Insert Chunk button on the toolbar or by pressing Ctrl+Alt+I.

When you save the notebook, an HTML file containing the code and output will be saved alongside it (click the Preview button or press Ctrl+Shift+K to preview the HTML file).

The preview shows you a rendered HTML copy of the contents of the editor. Consequently, unlike Knit, Preview does not run any R code chunks. Instead, the output of the chunk when it was last run in the editor is displayed.

DTTG practical starts here:

load the powdR package (it is already installed i.e avialable to load)


library(powdR)
powdR: Full Pattern Summation of X-Ray Powder Diffraction Data

where to find some help on functions in the package etc


help(package="powdR")

Ok lets try some prior determined full pattern fitting

first load the RockJock full pattern library which is included in the powdR package

data(rockjock)

check the RStudio Environment TAB to locate the rockjock library object

take a closer look at it if you want

powdR also comes with 8 diffraction patterns of RockJock mixtures, synthetic mineral mixtures made by Denni Eberl to test his RockJock spreadsheet program, [accessed via data(rockjock_mixtures)], and the known compositions of these mixtures provided in the rockjock_weights data [accessed via data(rockjock_weights)].


data("rockjock_mixtures")
data("rockjock_weights")

click on them in the Environment TAB for them to load fully

Once you have a powdRlib reference library and diffractogram(s) loaded into R, you have everything needed for quantitative analysis via full pattern summation. Full pattern summation in powdR is provided via the fps() function, whilst an automated version is provided in afps(). Details of the equations and routines implemented in fps() and afps() are provided in Butler and Hillier (2021a) and Butler and Hillier (2021b).

Here we will used the RockJock full pattern library and the measured diffraction patterns of the RockJock mixtures to test out some quantitaive phases analysis by prior determined full pattern fitting

for the first example we will fit Mixture 1 ’Mix1’using the fps (full pattern summation) powdR function

fps accepts a wide range of arguments that are detailed in the package documentation (see ?fps.powdRlib)

the example below passes the following seven arguments to fps():

1. lib is used to define the powdRlib object containing the reference patterns and their RIRs.
2. smpl is used to defined the data frame or XY object containing the sample diffractogram.
3. refs is used to define a string of phase IDs (lib$phases$phase_id) and/or phase names (lib$phases$phase_names) of the reference patterns to be used in the fitting process.
4. std is used to define the phase ID of the reference pattern to be used as the internal standard.
5. std_conc is used to define the concentration of the internal standard in weight %.
6. omit_std is used to define whether the internal standard is omitted from the output and phase concentrations recomputed accordingly.
7. align is used to define the maximum positive or negative shift in 2θ that is permitted during alignment of the sample to the reference pattern that is specified in the std argument.
data(rockjock_mixtures)

fit1 <- fps(lib = rockjock,
            smpl = rockjock_mixtures$Mix1,    #Mix1 in the rockjock_mixtures 
            refs = c("ORDERED_MICROCLINE",
                     "Plagioclase",
                     "KAOLINITE_DRY_BRANCH",
                     "MONTMORILLONITE_WYO",
                     "ILLITE_1M_RM30",
                     "CORUNDUM"),
            std = "CORUNDUM",
            std_conc = 20,
            omit_std = TRUE,
            align = 0.3)

-Aligning sample to the internal standard
-Interpolating library to same 2theta scale as aligned sample
-Optimising...
-Removing negative coefficients and reoptimising...
-Removing negative coefficients and reoptimising...
-Computing phase concentrations
-Using internal standard concentration of 20 % to compute phase concentrations
-Omitting internal standard from phase concentrations
***Full pattern summation complete***

Once computed, the fps() function produces a powdRfps object, which is a bundle of data in list format that contains the outputs (see ?fps.powdRlib).

Output was assigned above to the object ‘fit1’

summary(fit1)
                       Length Class     
tth                    2989   -none-    
fitted                 2989   -none-    
measured               2989   -none-    
residuals              2989   -none-    
phases                    4   data.frame
phases_grouped            2   data.frame
obj                       3   -none-    
weighted_pure_patterns    7   data.frame
coefficients              7   -none-    
inputs                   16   -none-    
                       Mode   
tth                    numeric
fitted                 numeric
measured               numeric
residuals              numeric
phases                 list   
phases_grouped         list   
obj                    numeric
weighted_pure_patterns list   
coefficients           numeric
inputs                 list   

The phase concentrations can be accessed in the phases data frame of the powdRfps object:

fit1$phases

notice that if the concentration of the internal standard is specified then the phase concentrations do not necessarily sum to 100 %:

sum(fit1$phases$phase_percent, na.rm = TRUE)
[1] 102.1108

Unlike other software where only certain phases can be used as an internal standard, any phase can be defined in powdR. For example, the rockjock_mixtures$Mix5 sample contains 20 % quartz (see data(rockjock_weights)), thus adding “QUARTZ” as the std argument results in this reference pattern becoming the internal standard instead.

fit2 <- fps(lib = rockjock,
            smpl = rockjock_mixtures$Mix5,
            refs = c("ORDERED_MICROCLINE",
                     "Plagioclase",
                     "KAOLINITE_DRY_BRANCH",
                     "MONTMORILLONITE_WYO",
                     "CORUNDUM",
                     "QUARTZ"),
            std = "QUARTZ",
            std_conc = 20,
            omit_std = TRUE,
            align = 0.3)

-Aligning sample to the internal standard
-Interpolating library to same 2theta scale as aligned sample
-Optimising...
-Removing negative coefficients and reoptimising...
-Removing negative coefficients and reoptimising...
-Computing phase concentrations
-Using internal standard concentration of 20 % to compute phase concentrations
-Omitting internal standard from phase concentrations
***Full pattern summation complete***
fit2$phases
sum(fit2$phases$phase_percent, na.rm = TRUE)
[1] 95.3805

In cases where an internal standard is not added to a sample, phase quantification can be achieved by assuming that all detectable phases can be identified and that they sum to 100 weight %. By setting the std_conc argument of fps() to NA, or leaving it out of the function call, it will be assumed that the sample has been prepared without an internal standard and the phase concentrations computed accordingly.

fit3 <- fps(lib = rockjock,
            smpl = rockjock_mixtures$Mix1,
            refs = c("ORDERED_MICROCLINE",
                     "Plagioclase",
                     "KAOLINITE_DRY_BRANCH",
                     "MONTMORILLONITE_WYO",
                     "ILLITE_1M_RM30",
                     "CORUNDUM"),
            std = "CORUNDUM",
            align = 0.3)

-Aligning sample to the internal standard
-Interpolating library to same 2theta scale as aligned sample
-Optimising...
-Removing negative coefficients and reoptimising...
-Removing negative coefficients and reoptimising...
-Computing phase concentrations
-Internal standard concentration unknown. Assuming phases sum to 100 %
***Full pattern summation complete***
fit3$phases
sum(fit3$phases$phase_percent) # this will be 100%
[1] 100

Plotting results powdRfps and powdRafps objects, derived from fps() (and afps() see later), respectively, is achieved using plot() (see ?plot.powdRfps and ?plot.powdRafps).

plot(fit1, wavelength = "Cu", interactive = FALSE)

objects can be further adjusted by the group, mode and xlim arguments. When the group argument is set to TRUE, the patterns within the fit are grouped and summed according to phase names, which can help simplify the plot:

The mode argument can be one of “fit” (the default), “residuals” or “both”, for example:

plot(fit1, wavelength = "Cu",
     mode = "both", xlim = c(20,30),
     interactive = TRUE)
Registered S3 method overwritten by 'data.table':
  method           from
  print.data.table     

Choosing which patterns to include to make a fit can be time consuming, as an alternative powdR also offers an apfs() function that will also select appropraite patterns from a (large) library and exclude others based on a limit of detection

Here the rockjock library, containing 169 reference patterns, will be used to quantify one of the samples in the rockjock_mixtures data. Note that when using afps(), omission of the refs argument in the function call will automatically result in all phases from the reference library being used in the fitting process.

#Produce the fit
a_fit1 <- afps(lib = rockjock,
               smpl = rockjock_mixtures$Mix1,
               std = "CORUNDUM",
               align = 0.3,
               lod = 1) #limit of detection weight % of the standard phase

-Using all reference patterns in the library
-Aligning sample to the internal standard
-Interpolating library to same 2theta scale as aligned sample
-Applying non-negative least squares
-Optimising...
-Removing negative coefficients and reoptimising...
-Calculating detection limits
-Removing phases below detection limit
-Reoptimising after removing crystalline phases below the limit of detection
-Computing phase concentrations
-Internal standard concentration unknown. Assuming phases sum to 100 %
***Automated full pattern summation complete***

Once computed, the afps function produces a powdRafps object, which is a bundle of data in list format that contains the outputs (see ?afps.powdRlib). When large libraries such a rockjock are used to quantify a given sample, the resulting output is likely contain several different reference patterns for a given mineral, for example:

table(a_fit1$phases$phase_name)

   Background      Corundum        Illite    K-feldspar 
            1             1             2             1 
    Kaolinite   Plagioclase Smectite (Di) 
            1             3             2 

The table illustrates that the resulting output contains 2 reference patterns for both illite and smectite, 3 patterns for plagioclase, and 1 pattern for each of the other phases selected by afps(). This information is grouped together and summed in the phases_grouped data frame within the powdRafps object:

a_fit1$phases_grouped

Note that the “background” phase in the output is simply a horizontal line that can account for shifts in background intensity, which can be useful to use in some cases. In the rockjock library, the background patterns have been given an exceptionally high RIR so that their quantified concentrations are near zero. These patterns are not in the original rockjock library

Mutiple sample quantification

The simplest way to quantify multiple samples via either fps() and afps() is by wrapping either of the functions in lapply() and supplying a list of diffractograms. The following example wraps the fps() function in lapply and applies the function to the first three items within the rockjock_mixtures data.

multi_fit <- lapply(rockjock_mixtures[1:3], fps,
                    lib = rockjock,
                    std = "CORUNDUM",
                    refs = c("ORDERED_MICROCLINE",
                             "LABRADORITE",
                             "KAOLINITE_DRY_BRANCH",
                             "MONTMORILLONITE_WYO",
                             "ILLITE_1M_RM30",
                             "CORUNDUM",
                             "QUARTZ"),
                    align = 0.3)

-Aligning sample to the internal standard
-Interpolating library to same 2theta scale as aligned sample
-Optimising...
-Computing phase concentrations
-Internal standard concentration unknown. Assuming phases sum to 100 %
***Full pattern summation complete***

-Aligning sample to the internal standard
-Interpolating library to same 2theta scale as aligned sample
-Optimising...
-Computing phase concentrations
-Internal standard concentration unknown. Assuming phases sum to 100 %
***Full pattern summation complete***

-Aligning sample to the internal standard
-Interpolating library to same 2theta scale as aligned sample
-Optimising...
-Removing negative coefficients and reoptimising...
-Computing phase concentrations
-Internal standard concentration unknown. Assuming phases sum to 100 %
***Full pattern summation complete***

Whilst lapply is a simple way to quantify multiple samples, the computation remains restricted to a single core. Computation time can be reduced many-fold by allowing different cores of your machine to process one sample at a time, which can be achieved using the doParallel and foreach packages:


#the below packages have been installed for you so just need loaded
#load the packages
library(foreach)
library(doParallel)
Loading required package: iterators
Loading required package: parallel
#Detect number of cores on machine
UseCores <- detectCores()

#Register the cluster using n - 1 cores 
cl <- makeCluster(UseCores-1)

registerDoParallel(cl)

#Use foreach loop and %dopar% to compute in parallel
multi_fit <- foreach(i = 1:3) %dopar%
  (powdR::fps(lib = rockjock,
               smpl = rockjock_mixtures[[i]],
               std = "CORUNDUM",
               refs = c("ORDERED_MICROCLINE",
                        "LABRADORITE",
                        "KAOLINITE_DRY_BRANCH",
                        "MONTMORILLONITE_WYO",
                        "ILLITE_1M_RM30",
                        "CORUNDUM",
                        "QUARTZ"),
               align = 0.3))

#name the items in the aquant_parallel list
names(multi_fit) <- names(rockjock_mixtures)[1:3]

#stop the cluster
stopCluster(cl)

When multiple samples are quantified it is often useful to report the phase concentrations of all of the samples in a single table. For a given list of powdRfps and/or powdRafps objects, the summarise_mineralogy() function yields such summary tables, for example:

summarise_mineralogy(multi_fit, type = "grouped", order = TRUE)

where type = “grouped” denotes that phases with the same phase_name will be summed together, and order = TRUE specifies that the columns will be ordered from most common to least common (assessed by the sum of each column). Using type = “all” instead would result in tabulation of all phase IDs.

In addition to the quantitative mineral data, three objective parameters that summarise the quality of the fit can be appended to the table via the logical rwp, r and delta arguments.

summarise_mineralogy(multi_fit, type = "grouped", order = TRUE,
                     rwp = TRUE, r = TRUE, delta = TRUE)

the summarised results can easily be exported to csv


# you may wish the check or set the default directory first to know where the file will be saved
# one way is to go to the set working directory menu item under session tab
min_summary <- summarise_mineralogy(multi_fit, type = "grouped", order = TRUE,
                     rwp = TRUE, r = TRUE, delta = TRUE)

write.csv(min_summary, "results-grouped.csv")  
LS0tDQp0aXRsZTogIlIgTm90ZWJvb2siDQpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sNCi0tLQ0KDQpUaGlzIGlzIGFuIFtSIE1hcmtkb3duXShodHRwOi8vcm1hcmtkb3duLnJzdHVkaW8uY29tKSBOb3RlYm9vay4gV2hlbiB5b3UgZXhlY3V0ZSBjb2RlIHdpdGhpbiB0aGUgbm90ZWJvb2ssIHRoZSByZXN1bHRzIGFwcGVhciBiZW5lYXRoIHRoZSBjb2RlLiANCg0KVHJ5IGV4ZWN1dGluZyB0aGlzIGNodW5rIGJ5IGNsaWNraW5nIHRoZSAqUnVuKiBidXR0b24gd2l0aGluIHRoZSBjaHVuayBvciBieSBwbGFjaW5nIHlvdXIgY3Vyc29yIGluc2lkZSBpdCBhbmQgcHJlc3NpbmcgKkN0cmwrU2hpZnQrRW50ZXIqLiANCg0KYGBge3J9DQpwbG90KGNhcnMpDQpgYGANCg0KQWRkIGEgbmV3IGNodW5rIGJ5IGNsaWNraW5nIHRoZSAqSW5zZXJ0IENodW5rKiBidXR0b24gb24gdGhlIHRvb2xiYXIgb3IgYnkgcHJlc3NpbmcgKkN0cmwrQWx0K0kqLg0KDQpXaGVuIHlvdSBzYXZlIHRoZSBub3RlYm9vaywgYW4gSFRNTCBmaWxlIGNvbnRhaW5pbmcgdGhlIGNvZGUgYW5kIG91dHB1dCB3aWxsIGJlIHNhdmVkIGFsb25nc2lkZSBpdCAoY2xpY2sgdGhlICpQcmV2aWV3KiBidXR0b24gb3IgcHJlc3MgKkN0cmwrU2hpZnQrSyogdG8gcHJldmlldyB0aGUgSFRNTCBmaWxlKS4NCg0KVGhlIHByZXZpZXcgc2hvd3MgeW91IGEgcmVuZGVyZWQgSFRNTCBjb3B5IG9mIHRoZSBjb250ZW50cyBvZiB0aGUgZWRpdG9yLiBDb25zZXF1ZW50bHksIHVubGlrZSAqS25pdCosICpQcmV2aWV3KiBkb2VzIG5vdCBydW4gYW55IFIgY29kZSBjaHVua3MuIEluc3RlYWQsIHRoZSBvdXRwdXQgb2YgdGhlIGNodW5rIHdoZW4gaXQgd2FzIGxhc3QgcnVuIGluIHRoZSBlZGl0b3IgaXMgZGlzcGxheWVkLg0KDQoNCiMgRFRURyBwcmFjdGljYWwgc3RhcnRzIGhlcmU6DQoNCiMjIyBsb2FkIHRoZSBwb3dkUiBwYWNrYWdlIChpdCBpcyBhbHJlYWR5IGluc3RhbGxlZCBpLmUgYXZpYWxhYmxlIHRvIGxvYWQpDQoNCmBgYHtyfQ0KDQpsaWJyYXJ5KHBvd2RSKQ0KDQpgYGANCg0KIyMjIHdoZXJlIHRvIGZpbmQgc29tZSBoZWxwIG9uIGZ1bmN0aW9ucyBpbiB0aGUgcGFja2FnZSBldGMNCg0KYGBge3J9DQoNCmhlbHAocGFja2FnZT0icG93ZFIiKQ0KDQpgYGANCg0KIyBPayBsZXRzIHRyeSBzb21lIHByaW9yIGRldGVybWluZWQgZnVsbCBwYXR0ZXJuIGZpdHRpbmcNCg0KIyMjIGZpcnN0IGxvYWQgdGhlIFJvY2tKb2NrIGZ1bGwgcGF0dGVybiBsaWJyYXJ5IHdoaWNoIGlzIGluY2x1ZGVkIGluIHRoZSBwb3dkUiBwYWNrYWdlDQoNCmBgYHtyfQ0KZGF0YShyb2Nram9jaykNCmBgYA0KDQojIyMgY2hlY2sgdGhlIFJTdHVkaW8gRW52aXJvbm1lbnQgVEFCIHRvIGxvY2F0ZSB0aGUgcm9ja2pvY2sgbGlicmFyeSBvYmplY3QNCiMjIyB0YWtlIGEgY2xvc2VyIGxvb2sgYXQgaXQgaWYgeW91IHdhbnQNCg0KDQojIyMgcG93ZFIgYWxzbyBjb21lcyB3aXRoIDggZGlmZnJhY3Rpb24gcGF0dGVybnMgb2YgUm9ja0pvY2sgbWl4dHVyZXMsIHN5bnRoZXRpYyBtaW5lcmFsIG1peHR1cmVzIG1hZGUgYnkgRGVubmkgRWJlcmwgdG8gdGVzdCBoaXMgUm9ja0pvY2sgc3ByZWFkc2hlZXQgcHJvZ3JhbSwgW2FjY2Vzc2VkIHZpYSBkYXRhKHJvY2tqb2NrX21peHR1cmVzKV0sIGFuZCB0aGUga25vd24gY29tcG9zaXRpb25zIG9mIHRoZXNlIG1peHR1cmVzIHByb3ZpZGVkIGluIHRoZSByb2Nram9ja193ZWlnaHRzIGRhdGEgW2FjY2Vzc2VkIHZpYSBkYXRhKHJvY2tqb2NrX3dlaWdodHMpXS4NCg0KDQoNCmBgYHtyfQ0KDQpkYXRhKCJyb2Nram9ja19taXh0dXJlcyIpDQpkYXRhKCJyb2Nram9ja193ZWlnaHRzIikNCg0KYGBgDQoNCiMjIyBjbGljayBvbiB0aGVtIGluIHRoZSBFbnZpcm9ubWVudCBUQUIgZm9yIHRoZW0gdG8gbG9hZCBmdWxseQ0KDQojIyMgT25jZSB5b3UgaGF2ZSBhIHBvd2RSbGliIHJlZmVyZW5jZSBsaWJyYXJ5IGFuZCBkaWZmcmFjdG9ncmFtKHMpIGxvYWRlZCBpbnRvIFIsIHlvdSBoYXZlIGV2ZXJ5dGhpbmcgbmVlZGVkIGZvciBxdWFudGl0YXRpdmUgYW5hbHlzaXMgdmlhIGZ1bGwgcGF0dGVybiBzdW1tYXRpb24uIEZ1bGwgcGF0dGVybiBzdW1tYXRpb24gaW4gcG93ZFIgaXMgcHJvdmlkZWQgdmlhIHRoZSBmcHMoKSBmdW5jdGlvbiwgd2hpbHN0IGFuIGF1dG9tYXRlZCB2ZXJzaW9uIGlzIHByb3ZpZGVkIGluIGFmcHMoKS4gRGV0YWlscyBvZiB0aGUgZXF1YXRpb25zIGFuZCByb3V0aW5lcyBpbXBsZW1lbnRlZCBpbiBmcHMoKSBhbmQgYWZwcygpIGFyZSBwcm92aWRlZCBpbiBCdXRsZXIgYW5kIEhpbGxpZXIgKDIwMjFhKSBhbmQgQnV0bGVyIGFuZCBIaWxsaWVyICgyMDIxYikuDQoNCg0KIyMjIEhlcmUgd2Ugd2lsbCB1c2VkIHRoZSBSb2NrSm9jayBmdWxsIHBhdHRlcm4gbGlicmFyeSBhbmQgdGhlIG1lYXN1cmVkIGRpZmZyYWN0aW9uIHBhdHRlcm5zIG9mIHRoZSBSb2NrSm9jayBtaXh0dXJlcyB0byB0ZXN0IG91dCBzb21lIHF1YW50aXRhaXZlIHBoYXNlcyBhbmFseXNpcyBieSBwcmlvciBkZXRlcm1pbmVkIGZ1bGwgcGF0dGVybiBmaXR0aW5nICANCg0KIyMjIGZvciB0aGUgZmlyc3QgZXhhbXBsZSB3ZSB3aWxsIGZpdCBNaXh0dXJlIDEgJ01peDEndXNpbmcgdGhlIGZwcyAoZnVsbCBwYXR0ZXJuIHN1bW1hdGlvbikgcG93ZFIgZnVuY3Rpb24NCg0KIyMjIGZwcyBhY2NlcHRzIGEgd2lkZSByYW5nZSBvZiBhcmd1bWVudHMgdGhhdCBhcmUgZGV0YWlsZWQgaW4gdGhlIHBhY2thZ2UgZG9jdW1lbnRhdGlvbiAoc2VlID9mcHMucG93ZFJsaWIpDQoNCiMjIyB0aGUgZXhhbXBsZSBiZWxvdyBwYXNzZXMgdGhlIGZvbGxvd2luZyBzZXZlbiBhcmd1bWVudHMgdG8gZnBzKCk6DQoNCiAgICAxLiBsaWIgaXMgdXNlZCB0byBkZWZpbmUgdGhlIHBvd2RSbGliIG9iamVjdCBjb250YWluaW5nIHRoZSByZWZlcmVuY2UgcGF0dGVybnMgYW5kIHRoZWlyIFJJUnMuDQogICAgMi4gc21wbCBpcyB1c2VkIHRvIGRlZmluZWQgdGhlIGRhdGEgZnJhbWUgb3IgWFkgb2JqZWN0IGNvbnRhaW5pbmcgdGhlIHNhbXBsZSBkaWZmcmFjdG9ncmFtLg0KICAgIDMuIHJlZnMgaXMgdXNlZCB0byBkZWZpbmUgYSBzdHJpbmcgb2YgcGhhc2UgSURzIChsaWIkcGhhc2VzJHBoYXNlX2lkKSBhbmQvb3IgcGhhc2UgbmFtZXMgKGxpYiRwaGFzZXMkcGhhc2VfbmFtZXMpIG9mIHRoZSByZWZlcmVuY2UgcGF0dGVybnMgdG8gYmUgdXNlZCBpbiB0aGUgZml0dGluZyBwcm9jZXNzLg0KICAgIDQuIHN0ZCBpcyB1c2VkIHRvIGRlZmluZSB0aGUgcGhhc2UgSUQgb2YgdGhlIHJlZmVyZW5jZSBwYXR0ZXJuIHRvIGJlIHVzZWQgYXMgdGhlIGludGVybmFsIHN0YW5kYXJkLg0KICAgIDUuIHN0ZF9jb25jIGlzIHVzZWQgdG8gZGVmaW5lIHRoZSBjb25jZW50cmF0aW9uIG9mIHRoZSBpbnRlcm5hbCBzdGFuZGFyZCBpbiB3ZWlnaHQgJS4NCiAgICA2LiBvbWl0X3N0ZCBpcyB1c2VkIHRvIGRlZmluZSB3aGV0aGVyIHRoZSBpbnRlcm5hbCBzdGFuZGFyZCBpcyBvbWl0dGVkIGZyb20gdGhlIG91dHB1dCBhbmQgcGhhc2UgY29uY2VudHJhdGlvbnMgcmVjb21wdXRlZCBhY2NvcmRpbmdseS4NCiAgICA3LiBhbGlnbiBpcyB1c2VkIHRvIGRlZmluZSB0aGUgbWF4aW11bSBwb3NpdGl2ZSBvciBuZWdhdGl2ZSBzaGlmdCBpbiAyzrggdGhhdCBpcyBwZXJtaXR0ZWQgZHVyaW5nIGFsaWdubWVudCBvZiB0aGUgc2FtcGxlIHRvIHRoZSByZWZlcmVuY2UgcGF0dGVybiB0aGF0IGlzIHNwZWNpZmllZCBpbiB0aGUgc3RkIGFyZ3VtZW50Lg0KICAgIA0KICAgIA0KYGBge3J9DQpkYXRhKHJvY2tqb2NrX21peHR1cmVzKQ0KDQpmaXQxIDwtIGZwcyhsaWIgPSByb2Nram9jaywNCiAgICAgICAgICAgIHNtcGwgPSByb2Nram9ja19taXh0dXJlcyRNaXgxLCAgICAjTWl4MSBpbiB0aGUgcm9ja2pvY2tfbWl4dHVyZXMgDQogICAgICAgICAgICByZWZzID0gYygiT1JERVJFRF9NSUNST0NMSU5FIiwNCiAgICAgICAgICAgICAgICAgICAgICJQbGFnaW9jbGFzZSIsDQogICAgICAgICAgICAgICAgICAgICAiS0FPTElOSVRFX0RSWV9CUkFOQ0giLA0KICAgICAgICAgICAgICAgICAgICAgIk1PTlRNT1JJTExPTklURV9XWU8iLA0KICAgICAgICAgICAgICAgICAgICAgIklMTElURV8xTV9STTMwIiwNCiAgICAgICAgICAgICAgICAgICAgICJDT1JVTkRVTSIpLA0KICAgICAgICAgICAgc3RkID0gIkNPUlVORFVNIiwNCiAgICAgICAgICAgIHN0ZF9jb25jID0gMjAsDQogICAgICAgICAgICBvbWl0X3N0ZCA9IFRSVUUsDQogICAgICAgICAgICBhbGlnbiA9IDAuMykNCmBgYA0KDQoNCiMjIyBPbmNlIGNvbXB1dGVkLCB0aGUgZnBzKCkgZnVuY3Rpb24gcHJvZHVjZXMgYSBwb3dkUmZwcyBvYmplY3QsIHdoaWNoIGlzIGEgYnVuZGxlIG9mIGRhdGEgaW4gbGlzdCBmb3JtYXQgdGhhdCBjb250YWlucyB0aGUgb3V0cHV0cyAoc2VlID9mcHMucG93ZFJsaWIpLg0KDQojIyMgT3V0cHV0IHdhcyBhc3NpZ25lZCBhYm92ZSB0byB0aGUgb2JqZWN0ICdmaXQxJw0KDQpgYGB7cn0NCnN1bW1hcnkoZml0MSkNCmBgYA0KDQoNCiMjIyBUaGUgcGhhc2UgY29uY2VudHJhdGlvbnMgY2FuIGJlIGFjY2Vzc2VkIGluIHRoZSBwaGFzZXMgZGF0YSBmcmFtZSBvZiB0aGUgcG93ZFJmcHMgb2JqZWN0Og0KDQoNCmBgYHtyfQ0KZml0MSRwaGFzZXMNCmBgYA0KDQojIyMgbm90aWNlIHRoYXQgaWYgdGhlIGNvbmNlbnRyYXRpb24gb2YgdGhlIGludGVybmFsIHN0YW5kYXJkIGlzIHNwZWNpZmllZCB0aGVuIHRoZSBwaGFzZSBjb25jZW50cmF0aW9ucyBkbyBub3QgbmVjZXNzYXJpbHkgc3VtIHRvIDEwMCAlOg0KDQpgYGB7cn0NCnN1bShmaXQxJHBoYXNlcyRwaGFzZV9wZXJjZW50LCBuYS5ybSA9IFRSVUUpDQpgYGANCg0KDQojIyMgVW5saWtlIG90aGVyIHNvZnR3YXJlIHdoZXJlIG9ubHkgY2VydGFpbiBwaGFzZXMgY2FuIGJlIHVzZWQgYXMgYW4gaW50ZXJuYWwgc3RhbmRhcmQsIGFueSBwaGFzZSBjYW4gYmUgZGVmaW5lZCBpbiBwb3dkUi4gRm9yIGV4YW1wbGUsIHRoZSByb2Nram9ja19taXh0dXJlcyRNaXg1IHNhbXBsZSBjb250YWlucyAyMCAlIHF1YXJ0eiAoc2VlIGRhdGEocm9ja2pvY2tfd2VpZ2h0cykpLCB0aHVzIGFkZGluZyAiUVVBUlRaIiBhcyB0aGUgc3RkIGFyZ3VtZW50IHJlc3VsdHMgaW4gdGhpcyByZWZlcmVuY2UgcGF0dGVybiBiZWNvbWluZyB0aGUgaW50ZXJuYWwgc3RhbmRhcmQgaW5zdGVhZC4NCg0KDQoNCmBgYHtyfQ0KZml0MiA8LSBmcHMobGliID0gcm9ja2pvY2ssDQogICAgICAgICAgICBzbXBsID0gcm9ja2pvY2tfbWl4dHVyZXMkTWl4NSwNCiAgICAgICAgICAgIHJlZnMgPSBjKCJPUkRFUkVEX01JQ1JPQ0xJTkUiLA0KICAgICAgICAgICAgICAgICAgICAgIlBsYWdpb2NsYXNlIiwNCiAgICAgICAgICAgICAgICAgICAgICJLQU9MSU5JVEVfRFJZX0JSQU5DSCIsDQogICAgICAgICAgICAgICAgICAgICAiTU9OVE1PUklMTE9OSVRFX1dZTyIsDQogICAgICAgICAgICAgICAgICAgICAiQ09SVU5EVU0iLA0KICAgICAgICAgICAgICAgICAgICAgIlFVQVJUWiIpLA0KICAgICAgICAgICAgc3RkID0gIlFVQVJUWiIsDQogICAgICAgICAgICBzdGRfY29uYyA9IDIwLA0KICAgICAgICAgICAgb21pdF9zdGQgPSBUUlVFLA0KICAgICAgICAgICAgYWxpZ24gPSAwLjMpDQpgYGANCg0KDQpgYGB7cn0NCmZpdDIkcGhhc2VzDQpgYGANCg0KYGBge3J9DQpzdW0oZml0MiRwaGFzZXMkcGhhc2VfcGVyY2VudCwgbmEucm0gPSBUUlVFKQ0KYGBgDQoNCg0KIyMjIEluIGNhc2VzIHdoZXJlIGFuIGludGVybmFsIHN0YW5kYXJkIGlzIG5vdCBhZGRlZCB0byBhIHNhbXBsZSwgcGhhc2UgcXVhbnRpZmljYXRpb24gY2FuIGJlIGFjaGlldmVkIGJ5IGFzc3VtaW5nIHRoYXQgYWxsIGRldGVjdGFibGUgcGhhc2VzIGNhbiBiZSBpZGVudGlmaWVkIGFuZCB0aGF0IHRoZXkgc3VtIHRvIDEwMCB3ZWlnaHQgJS4gQnkgc2V0dGluZyB0aGUgc3RkX2NvbmMgYXJndW1lbnQgb2YgZnBzKCkgdG8gTkEsIG9yIGxlYXZpbmcgaXQgb3V0IG9mIHRoZSBmdW5jdGlvbiBjYWxsLCBpdCB3aWxsIGJlIGFzc3VtZWQgdGhhdCB0aGUgc2FtcGxlIGhhcyBiZWVuIHByZXBhcmVkIHdpdGhvdXQgYW4gaW50ZXJuYWwgc3RhbmRhcmQgYW5kIHRoZSBwaGFzZSBjb25jZW50cmF0aW9ucyBjb21wdXRlZCBhY2NvcmRpbmdseS4NCg0KYGBge3J9DQpmaXQzIDwtIGZwcyhsaWIgPSByb2Nram9jaywNCiAgICAgICAgICAgIHNtcGwgPSByb2Nram9ja19taXh0dXJlcyRNaXgxLA0KICAgICAgICAgICAgcmVmcyA9IGMoIk9SREVSRURfTUlDUk9DTElORSIsDQogICAgICAgICAgICAgICAgICAgICAiUGxhZ2lvY2xhc2UiLA0KICAgICAgICAgICAgICAgICAgICAgIktBT0xJTklURV9EUllfQlJBTkNIIiwNCiAgICAgICAgICAgICAgICAgICAgICJNT05UTU9SSUxMT05JVEVfV1lPIiwNCiAgICAgICAgICAgICAgICAgICAgICJJTExJVEVfMU1fUk0zMCIsDQogICAgICAgICAgICAgICAgICAgICAiQ09SVU5EVU0iKSwNCiAgICAgICAgICAgIHN0ZCA9ICJDT1JVTkRVTSIsDQogICAgICAgICAgICBhbGlnbiA9IDAuMykNCmBgYA0KDQpgYGB7cn0NCmZpdDMkcGhhc2VzDQpgYGANCg0KDQpgYGB7cn0NCnN1bShmaXQzJHBoYXNlcyRwaGFzZV9wZXJjZW50KSAjIHRoaXMgd2lsbCBiZSAxMDAlDQpgYGANCg0KDQojIyMgUGxvdHRpbmcgcmVzdWx0cyBwb3dkUmZwcyBhbmQgcG93ZFJhZnBzIG9iamVjdHMsIGRlcml2ZWQgZnJvbSBmcHMoKSAoYW5kIGFmcHMoKSBzZWUgbGF0ZXIpLCByZXNwZWN0aXZlbHksIGlzIGFjaGlldmVkIHVzaW5nIHBsb3QoKSAoc2VlID9wbG90LnBvd2RSZnBzIGFuZCA/cGxvdC5wb3dkUmFmcHMpLg0KDQoNCmBgYHtyfQ0KcGxvdChmaXQxLCB3YXZlbGVuZ3RoID0gIkN1IiwgaW50ZXJhY3RpdmUgPSBGQUxTRSkNCmBgYA0KDQoNCg0KIyMjIG9iamVjdHMgY2FuIGJlIGZ1cnRoZXIgYWRqdXN0ZWQgYnkgdGhlIGdyb3VwLCBtb2RlIGFuZCB4bGltIGFyZ3VtZW50cy4gV2hlbiB0aGUgZ3JvdXAgYXJndW1lbnQgaXMgc2V0IHRvIFRSVUUsIHRoZSBwYXR0ZXJucyB3aXRoaW4gdGhlIGZpdCBhcmUgZ3JvdXBlZCBhbmQgc3VtbWVkIGFjY29yZGluZyB0byBwaGFzZSBuYW1lcywgd2hpY2ggY2FuIGhlbHAgc2ltcGxpZnkgdGhlIHBsb3Q6DQoNCiMjIyBUaGUgbW9kZSBhcmd1bWVudCBjYW4gYmUgb25lIG9mICJmaXQiICh0aGUgZGVmYXVsdCksICJyZXNpZHVhbHMiIG9yICJib3RoIiwgZm9yIGV4YW1wbGU6DQoNCg0KYGBge3J9DQpwbG90KGZpdDEsIHdhdmVsZW5ndGggPSAiQ3UiLA0KICAgICBtb2RlID0gImJvdGgiLCB4bGltID0gYygyMCwzMCksDQogICAgIGludGVyYWN0aXZlID0gVFJVRSkNCmBgYA0KDQoNCiMjIyBDaG9vc2luZyB3aGljaCBwYXR0ZXJucyB0byBpbmNsdWRlIHRvIG1ha2UgYSBmaXQgY2FuIGJlIHRpbWUgY29uc3VtaW5nLCBhcyBhbiBhbHRlcm5hdGl2ZSBwb3dkUiBhbHNvIG9mZmVycyBhbiBhcGZzKCkgZnVuY3Rpb24gdGhhdCB3aWxsIGFsc28gc2VsZWN0IGFwcHJvcHJhaXRlIHBhdHRlcm5zIGZyb20gYSAobGFyZ2UpIGxpYnJhcnkgYW5kIGV4Y2x1ZGUgb3RoZXJzIGJhc2VkIG9uIGEgbGltaXQgb2YgZGV0ZWN0aW9uDQoNCiMjIyBIZXJlIHRoZSByb2Nram9jayBsaWJyYXJ5LCBjb250YWluaW5nIDE2OSByZWZlcmVuY2UgcGF0dGVybnMsIHdpbGwgYmUgdXNlZCB0byBxdWFudGlmeSBvbmUgb2YgdGhlIHNhbXBsZXMgaW4gdGhlIHJvY2tqb2NrX21peHR1cmVzIGRhdGEuIE5vdGUgdGhhdCB3aGVuIHVzaW5nIGFmcHMoKSwgb21pc3Npb24gb2YgdGhlIHJlZnMgYXJndW1lbnQgaW4gdGhlIGZ1bmN0aW9uIGNhbGwgd2lsbCBhdXRvbWF0aWNhbGx5IHJlc3VsdCBpbiBhbGwgcGhhc2VzIGZyb20gdGhlIHJlZmVyZW5jZSBsaWJyYXJ5IGJlaW5nIHVzZWQgaW4gdGhlIGZpdHRpbmcgcHJvY2Vzcy4NCg0KYGBge3J9DQojUHJvZHVjZSB0aGUgZml0DQphX2ZpdDEgPC0gYWZwcyhsaWIgPSByb2Nram9jaywNCiAgICAgICAgICAgICAgIHNtcGwgPSByb2Nram9ja19taXh0dXJlcyRNaXgxLA0KICAgICAgICAgICAgICAgc3RkID0gIkNPUlVORFVNIiwNCiAgICAgICAgICAgICAgIGFsaWduID0gMC4zLA0KICAgICAgICAgICAgICAgbG9kID0gMSkgI2xpbWl0IG9mIGRldGVjdGlvbiB3ZWlnaHQgJSBvZiB0aGUgc3RhbmRhcmQgcGhhc2UNCmBgYA0KDQoNCiMjIyBPbmNlIGNvbXB1dGVkLCB0aGUgYWZwcyBmdW5jdGlvbiBwcm9kdWNlcyBhIHBvd2RSYWZwcyBvYmplY3QsIHdoaWNoIGlzIGEgYnVuZGxlIG9mIGRhdGEgaW4gbGlzdCBmb3JtYXQgdGhhdCBjb250YWlucyB0aGUgb3V0cHV0cyAoc2VlID9hZnBzLnBvd2RSbGliKS4gV2hlbiBsYXJnZSBsaWJyYXJpZXMgc3VjaCBhIHJvY2tqb2NrIGFyZSB1c2VkIHRvIHF1YW50aWZ5IGEgZ2l2ZW4gc2FtcGxlLCB0aGUgcmVzdWx0aW5nIG91dHB1dCBpcyBsaWtlbHkgY29udGFpbiBzZXZlcmFsIGRpZmZlcmVudCByZWZlcmVuY2UgcGF0dGVybnMgZm9yIGEgZ2l2ZW4gbWluZXJhbCwgZm9yIGV4YW1wbGU6DQoNCmBgYHtyfQ0KdGFibGUoYV9maXQxJHBoYXNlcyRwaGFzZV9uYW1lKQ0KYGBgDQoNCg0KIyMjIFRoZSB0YWJsZSBpbGx1c3RyYXRlcyB0aGF0IHRoZSByZXN1bHRpbmcgb3V0cHV0IGNvbnRhaW5zIDIgcmVmZXJlbmNlIHBhdHRlcm5zIGZvciBib3RoIGlsbGl0ZSBhbmQgc21lY3RpdGUsIDMgcGF0dGVybnMgZm9yIHBsYWdpb2NsYXNlLCBhbmQgMSBwYXR0ZXJuIGZvciBlYWNoIG9mIHRoZSBvdGhlciBwaGFzZXMgc2VsZWN0ZWQgYnkgYWZwcygpLiBUaGlzIGluZm9ybWF0aW9uIGlzIGdyb3VwZWQgdG9nZXRoZXIgYW5kIHN1bW1lZCBpbiB0aGUgcGhhc2VzX2dyb3VwZWQgZGF0YSBmcmFtZSB3aXRoaW4gdGhlIHBvd2RSYWZwcyBvYmplY3Q6DQoNCmBgYHtyfQ0KYV9maXQxJHBoYXNlc19ncm91cGVkDQpgYGANCg0KIyMjIE5vdGUgdGhhdCB0aGUg4oCcYmFja2dyb3VuZOKAnSBwaGFzZSBpbiB0aGUgb3V0cHV0IGlzIHNpbXBseSBhIGhvcml6b250YWwgbGluZSB0aGF0IGNhbiBhY2NvdW50IGZvciBzaGlmdHMgaW4gYmFja2dyb3VuZCBpbnRlbnNpdHksIHdoaWNoIGNhbiBiZSB1c2VmdWwgdG8gdXNlIGluIHNvbWUgY2FzZXMuIEluIHRoZSByb2Nram9jayBsaWJyYXJ5LCB0aGUgYmFja2dyb3VuZCBwYXR0ZXJucyBoYXZlIGJlZW4gZ2l2ZW4gYW4gZXhjZXB0aW9uYWxseSBoaWdoIFJJUiBzbyB0aGF0IHRoZWlyIHF1YW50aWZpZWQgY29uY2VudHJhdGlvbnMgYXJlIG5lYXIgemVyby4gVGhlc2UgcGF0dGVybnMgYXJlIG5vdCBpbiB0aGUgb3JpZ2luYWwgcm9ja2pvY2sgbGlicmFyeQ0KDQoNCiMgTXV0aXBsZSBzYW1wbGUgcXVhbnRpZmljYXRpb24NCg0KIyMjIFRoZSBzaW1wbGVzdCB3YXkgdG8gcXVhbnRpZnkgbXVsdGlwbGUgc2FtcGxlcyB2aWEgZWl0aGVyIGZwcygpIGFuZCBhZnBzKCkgaXMgYnkgd3JhcHBpbmcgZWl0aGVyIG9mIHRoZSBmdW5jdGlvbnMgaW4gbGFwcGx5KCkgYW5kIHN1cHBseWluZyBhIGxpc3Qgb2YgZGlmZnJhY3RvZ3JhbXMuIFRoZSBmb2xsb3dpbmcgZXhhbXBsZSB3cmFwcyB0aGUgZnBzKCkgZnVuY3Rpb24gaW4gbGFwcGx5IGFuZCBhcHBsaWVzIHRoZSBmdW5jdGlvbiB0byB0aGUgZmlyc3QgdGhyZWUgaXRlbXMgd2l0aGluIHRoZSByb2Nram9ja19taXh0dXJlcyBkYXRhLg0KDQoNCmBgYHtyfQ0KbXVsdGlfZml0IDwtIGxhcHBseShyb2Nram9ja19taXh0dXJlc1sxOjNdLCBmcHMsDQogICAgICAgICAgICAgICAgICAgIGxpYiA9IHJvY2tqb2NrLA0KICAgICAgICAgICAgICAgICAgICBzdGQgPSAiQ09SVU5EVU0iLA0KICAgICAgICAgICAgICAgICAgICByZWZzID0gYygiT1JERVJFRF9NSUNST0NMSU5FIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkxBQlJBRE9SSVRFIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIktBT0xJTklURV9EUllfQlJBTkNIIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIk1PTlRNT1JJTExPTklURV9XWU8iLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiSUxMSVRFXzFNX1JNMzAiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiQ09SVU5EVU0iLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiUVVBUlRaIiksDQogICAgICAgICAgICAgICAgICAgIGFsaWduID0gMC4zKQ0KYGBgDQoNCg0KIyMjIFdoaWxzdCBsYXBwbHkgaXMgYSBzaW1wbGUgd2F5IHRvIHF1YW50aWZ5IG11bHRpcGxlIHNhbXBsZXMsIHRoZSBjb21wdXRhdGlvbiByZW1haW5zIHJlc3RyaWN0ZWQgdG8gYSBzaW5nbGUgY29yZS4gQ29tcHV0YXRpb24gdGltZSBjYW4gYmUgcmVkdWNlZCBtYW55LWZvbGQgYnkgYWxsb3dpbmcgZGlmZmVyZW50IGNvcmVzIG9mIHlvdXIgbWFjaGluZSB0byBwcm9jZXNzIG9uZSBzYW1wbGUgYXQgYSB0aW1lLCB3aGljaCBjYW4gYmUgYWNoaWV2ZWQgdXNpbmcgdGhlIGRvUGFyYWxsZWwgYW5kIGZvcmVhY2ggcGFja2FnZXM6DQoNCmBgYHtyfQ0KDQojdGhlIGJlbG93IHBhY2thZ2VzIGhhdmUgYmVlbiBpbnN0YWxsZWQgZm9yIHlvdSBzbyBqdXN0IG5lZWQgbG9hZGVkDQojbG9hZCB0aGUgcGFja2FnZXMNCmxpYnJhcnkoZm9yZWFjaCkNCmxpYnJhcnkoZG9QYXJhbGxlbCkNCg0KI0RldGVjdCBudW1iZXIgb2YgY29yZXMgb24gbWFjaGluZQ0KVXNlQ29yZXMgPC0gZGV0ZWN0Q29yZXMoKQ0KDQojUmVnaXN0ZXIgdGhlIGNsdXN0ZXIgdXNpbmcgbiAtIDEgY29yZXMgDQpjbCA8LSBtYWtlQ2x1c3RlcihVc2VDb3Jlcy0xKQ0KDQpyZWdpc3RlckRvUGFyYWxsZWwoY2wpDQoNCiNVc2UgZm9yZWFjaCBsb29wIGFuZCAlZG9wYXIlIHRvIGNvbXB1dGUgaW4gcGFyYWxsZWwNCm11bHRpX2ZpdCA8LSBmb3JlYWNoKGkgPSAxOjMpICVkb3BhciUNCiAgKHBvd2RSOjpmcHMobGliID0gcm9ja2pvY2ssDQogICAgICAgICAgICAgICBzbXBsID0gcm9ja2pvY2tfbWl4dHVyZXNbW2ldXSwNCiAgICAgICAgICAgICAgIHN0ZCA9ICJDT1JVTkRVTSIsDQogICAgICAgICAgICAgICByZWZzID0gYygiT1JERVJFRF9NSUNST0NMSU5FIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICJMQUJSQURPUklURSIsDQogICAgICAgICAgICAgICAgICAgICAgICAiS0FPTElOSVRFX0RSWV9CUkFOQ0giLA0KICAgICAgICAgICAgICAgICAgICAgICAgIk1PTlRNT1JJTExPTklURV9XWU8iLA0KICAgICAgICAgICAgICAgICAgICAgICAgIklMTElURV8xTV9STTMwIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICJDT1JVTkRVTSIsDQogICAgICAgICAgICAgICAgICAgICAgICAiUVVBUlRaIiksDQogICAgICAgICAgICAgICBhbGlnbiA9IDAuMykpDQoNCiNuYW1lIHRoZSBpdGVtcyBpbiB0aGUgYXF1YW50X3BhcmFsbGVsIGxpc3QNCm5hbWVzKG11bHRpX2ZpdCkgPC0gbmFtZXMocm9ja2pvY2tfbWl4dHVyZXMpWzE6M10NCg0KI3N0b3AgdGhlIGNsdXN0ZXINCnN0b3BDbHVzdGVyKGNsKQ0KYGBgDQoNCg0KIyMjIFdoZW4gbXVsdGlwbGUgc2FtcGxlcyBhcmUgcXVhbnRpZmllZCBpdCBpcyBvZnRlbiB1c2VmdWwgdG8gcmVwb3J0IHRoZSBwaGFzZSBjb25jZW50cmF0aW9ucyBvZiBhbGwgb2YgdGhlIHNhbXBsZXMgaW4gYSBzaW5nbGUgdGFibGUuIEZvciBhIGdpdmVuIGxpc3Qgb2YgcG93ZFJmcHMgYW5kL29yIHBvd2RSYWZwcyBvYmplY3RzLCB0aGUgc3VtbWFyaXNlX21pbmVyYWxvZ3koKSBmdW5jdGlvbiB5aWVsZHMgc3VjaCBzdW1tYXJ5IHRhYmxlcywgZm9yIGV4YW1wbGU6DQoNCg0KYGBge3J9DQpzdW1tYXJpc2VfbWluZXJhbG9neShtdWx0aV9maXQsIHR5cGUgPSAiZ3JvdXBlZCIsIG9yZGVyID0gVFJVRSkNCmBgYA0KDQojIyMgd2hlcmUgdHlwZSA9ICJncm91cGVkIiBkZW5vdGVzIHRoYXQgcGhhc2VzIHdpdGggdGhlIHNhbWUgcGhhc2VfbmFtZSB3aWxsIGJlIHN1bW1lZCB0b2dldGhlciwgYW5kIG9yZGVyID0gVFJVRSBzcGVjaWZpZXMgdGhhdCB0aGUgY29sdW1ucyB3aWxsIGJlIG9yZGVyZWQgZnJvbSBtb3N0IGNvbW1vbiB0byBsZWFzdCBjb21tb24gKGFzc2Vzc2VkIGJ5IHRoZSBzdW0gb2YgZWFjaCBjb2x1bW4pLiBVc2luZyB0eXBlID0gImFsbCIgaW5zdGVhZCB3b3VsZCByZXN1bHQgaW4gdGFidWxhdGlvbiBvZiBhbGwgcGhhc2UgSURzLg0KDQojIyMgSW4gYWRkaXRpb24gdG8gdGhlIHF1YW50aXRhdGl2ZSBtaW5lcmFsIGRhdGEsIHRocmVlIG9iamVjdGl2ZSBwYXJhbWV0ZXJzIHRoYXQgc3VtbWFyaXNlIHRoZSBxdWFsaXR5IG9mIHRoZSBmaXQgY2FuIGJlIGFwcGVuZGVkIHRvIHRoZSB0YWJsZSB2aWEgdGhlIGxvZ2ljYWwgcndwLCByIGFuZCBkZWx0YSBhcmd1bWVudHMuDQoNCmBgYHtyfQ0Kc3VtbWFyaXNlX21pbmVyYWxvZ3kobXVsdGlfZml0LCB0eXBlID0gImdyb3VwZWQiLCBvcmRlciA9IFRSVUUsDQogICAgICAgICAgICAgICAgICAgICByd3AgPSBUUlVFLCByID0gVFJVRSwgZGVsdGEgPSBUUlVFKQ0KYGBgDQoNCiMjIyB0aGUgc3VtbWFyaXNlZCByZXN1bHRzIGNhbiBlYXNpbHkgYmUgZXhwb3J0ZWQgdG8gY3N2DQoNCmBgYHtyfQ0KDQojIHlvdSBtYXkgd2lzaCB0aGUgY2hlY2sgb3Igc2V0IHRoZSBkZWZhdWx0IGRpcmVjdG9yeSBmaXJzdCB0byBrbm93IHdoZXJlIHRoZSBmaWxlIHdpbGwgYmUgc2F2ZWQNCiMgb25lIHdheSBpcyB0byBnbyB0byB0aGUgc2V0IHdvcmtpbmcgZGlyZWN0b3J5IG1lbnUgaXRlbSB1bmRlciBzZXNzaW9uIHRhYg0KbWluX3N1bW1hcnkgPC0gc3VtbWFyaXNlX21pbmVyYWxvZ3kobXVsdGlfZml0LCB0eXBlID0gImdyb3VwZWQiLCBvcmRlciA9IFRSVUUsDQogICAgICAgICAgICAgICAgICAgICByd3AgPSBUUlVFLCByID0gVFJVRSwgZGVsdGEgPSBUUlVFKQ0KDQp3cml0ZS5jc3YobWluX3N1bW1hcnksICJyZXN1bHRzLWdyb3VwZWQuY3N2IikgIA0KYGBgDQoNCg0KDQoNCg==